<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
  ~ distributed with this work for additional information
  ~ regarding copyright ownership.  The ASF licenses this file
  ~ to you under the Apache License, Version 2.0 (the
  ~ "License"); you may not use this file except in compliance
  ~ with the License.  You may obtain a copy of the License at
  ~
  ~   http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing,
  ~ software distributed under the License is distributed on an
  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  ~ KIND, either express or implied.  See the License for the
  ~ specific language governing permissions and limitations
  ~ under the License.
-->

<script lang="ts" setup>
  import { computed, onMounted, ref, toRaw, toRefs, watch } from 'vue'
  import { Emits, FormState, Props } from './types'
  import { useLocaleStore } from '@/store/locale'
  import { storeToRefs } from 'pinia'

  const props = withDefaults(defineProps<Props>(), {
    showButton: true,
    disabledItems: null, // Has higher priority than controlProps of formItems
    hiddenItems: () => [],
    labelCol: () => {
      return { span: 5 }
    },
    wrapperCol: () => {
      return { span: 14 }
    },
    formOptions: () => {
      return {
        hideOk: false,
        hideCancel: false,
        okText: 'Submit',
        cancelText: 'Reset'
      }
    }
  })

  const emits = defineEmits<Emits>()

  const localeStore = useLocaleStore()
  const { locale } = storeToRefs(localeStore)
  const formRef = ref()
  const formState = ref<FormState>({})
  const tmpCacheFormState = ref<FormState>({})
  const optionMap = ref<Record<string, unknown>>({})
  const formItemEvents = ref<Record<string, unknown>>({})
  const { formValue } = toRefs(props)

  const ruleFormTmpResolve = computed({
    get() {
      const len = Object.keys(formValue.value).length
      return len ? formValue.value : tmpCacheFormState.value
    },
    set(val) {
      tmpCacheFormState.value = val || {}
      emits('update:formValue', val)
    }
  })

  watch(
    formState,
    (newState) => {
      emits('update:formValue', newState)
    },
    {
      deep: true
    }
  )

  watch(locale, () => {
    formRef.value.clearValidate()
  })

  const initForm = () => {
    const newForm: FormState = {}
    props.formItems.forEach((item) => {
      const { field, defaultValue } = item
      newForm[field] = defaultValue || undefined
    })
    Object.assign(formState.value, {
      ...newForm,
      ...ruleFormTmpResolve.value
    })
  }

  const getFormValidation = async () => {
    try {
      await formRef.value?.validateFields()
      return Promise.resolve(true)
    } catch (error) {
      console.log('Failed:', error)
      return Promise.resolve(false)
    }
  }

  const setOptions = (field: string, list: unknown[]) => {
    optionMap.value[field] = list
  }

  const setFormItemEvents = (field: string, events: any) => {
    formItemEvents.value[field] = events
  }

  const setFormValue = <T,>(form: FormState<T>) => {
    const oldForm = { ...toRaw(formState.value) }
    formState.value = { ...oldForm, ...form }
  }

  const resetForm = () => {
    formRef.value.resetFields()
  }

  const onSubmit = async () => {
    try {
      await formRef.value.validateFields()
      emits('onSubmit', true)
    } catch (error) {
      console.log('Failed:', error)
      emits('onSubmit', false)
    }
  }

  const onReset = () => {
    resetForm()
  }

  onMounted(() => {
    initForm()
  })

  defineExpose({
    resetForm,
    getFormValidation,
    setOptions,
    setFormItemEvents,
    setFormValue
  })
</script>

<template>
  <div>
    <a-form
      ref="formRef"
      label-align="left"
      :colon="false"
      :model="formState"
      :label-col="props.labelCol"
      :wrapper-col="props.wrapperCol"
      :disabled="props.formDisabled"
    >
      <div v-for="item in props.formItems" :key="item.field">
        <slot
          v-if="!props.hiddenItems.includes(item.field)"
          :="{ item, state: formState }"
          :name="item.slot ?? item.field"
        >
          <a-form-item v-bind="item.formItemProps">
            <!-- input -->
            <a-input
              v-if="item.type == 'input'"
              v-model:value="formState[item.field]"
              v-bind="item.controlProps"
              :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled"
              v-on="formItemEvents[item.field] || {}"
            />

            <a-input-password
              v-if="item.type == 'inputPassword'"
              v-model:value="formState[item.field]"
              v-bind="item.controlProps"
              :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled"
              v-on="formItemEvents[item.field] || {}"
            />

            <!-- textarea  -->
            <a-textarea
              v-if="item.type == 'textarea'"
              v-model:value="formState[item.field]"
              v-bind="item.controlProps"
              :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled"
              v-on="formItemEvents[item.field] || {}"
            />

            <!-- select -->
            <a-select
              v-if="item.type == 'select'"
              v-model:value="formState[item.field]"
              v-bind="item.controlProps"
              :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled"
              v-on="formItemEvents[item.field] || {}"
            >
              <a-select-option
                v-for="(child, childIndex) in optionMap[item.field] || item.defaultOptionsMap || []"
                :key="childIndex"
                :value="typeof child === 'string' ? child : child[(item.fieldMap && item.fieldMap.value) || 'value']"
              >
                {{ typeof child === 'string' ? child : child[(item.fieldMap && item.fieldMap.label) || 'label'] }}
              </a-select-option>
            </a-select>

            <!-- radio -->
            <a-radio-group
              v-if="item.type == 'radio'"
              v-model:value="formState[item.field]"
              v-bind="item.controlProps"
              :disabled="disabledItems ? disabledItems.includes(item.field) : item.controlProps?.disabled"
              v-on="formItemEvents[item.field] || {}"
            >
              <a-radio
                v-for="(child, childIndex) in optionMap[item.field] || item.defaultOptionsMap || []"
                :key="childIndex"
                :value="child[(item.fieldMap && item.fieldMap.value) || 'value']"
              >
                {{ child[(item.fieldMap && item.fieldMap.label) || 'label'] }}
              </a-radio>
            </a-radio-group>
          </a-form-item>
        </slot>
      </div>
      <a-form-item v-if="showButton">
        <slot name="actions" :on-submit="onSubmit" :on-reset="onReset">
          <a-space>
            <a-button type="primary" @click="onSubmit">
              {{ props.formOptions.okText }}
            </a-button>
            <a-button @click="onReset">
              {{ props.formOptions.hideCancel }}
            </a-button>
          </a-space>
        </slot>
      </a-form-item>
    </a-form>
  </div>
</template>
